package com.huaruan.youdi.http.okhttp;

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.huaruan.youdi.models.HttpStateModel;
import com.huaruan.youdi.utils.AppUtils;
import com.huaruan.youdi.utils.StringUtils;
import com.huaruan.youdi.utils.UserUtils;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.URLEncoder;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Cookie;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;

/**
 * Created by dingke on 2020/9/14.
 */
public class HttpUtils {
    private static final String TAG = "HTTP";
    private static final Handler handler;
    private static final int TIME_OUT = 10;
    private static final String neterrorStr = "网络故障，请稍后重试！";
    public static final String fetchDataErrorStr = "请求数据失败,请稍后重试";
    private static final OkHttpClient mOkHttpClient;

    static {
        X509TrustManager trustManager = new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws
                    CertificateException {

            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
            }
        };
        SSLSocketFactory sslSocketFactory;
        try {
            SSLContext sslContext;
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, new X509TrustManager[]{trustManager}, null);
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (GeneralSecurityException e) {
            throw new RuntimeException(e);
        }


        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            private StringBuilder messages = new StringBuilder();
            private final int JSON_INDENT = 2;

            @Override
            public void log(String message) {
                try {
                    if (message.startsWith("{") && message.endsWith("}")) {
                        JSONObject jsonObject = new JSONObject(message);
                        message = jsonObject.toString(JSON_INDENT);
                    } else if (message.startsWith("[") && message.endsWith("]")) {
                        JSONArray jsonArray = new JSONArray(message);
                        message = jsonArray.toString(JSON_INDENT);
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                messages.append(message);
                messages.append("\n");
                if (message.startsWith("<-- END HTTP")) {
                    Log.e("okhttp", messages.toString());
                    messages.delete(0, messages.length());
                }
            }
        });
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        mOkHttpClient = new OkHttpClient.Builder()
                .connectTimeout(TIME_OUT, TimeUnit.SECONDS)
                .writeTimeout(TIME_OUT, TimeUnit.SECONDS)
                .readTimeout(TIME_OUT, TimeUnit.SECONDS)
                .sslSocketFactory(sslSocketFactory, trustManager)
                .addNetworkInterceptor(interceptor)
                .build();


        handler = new Handler(Looper.getMainLooper());
    }

    //新版本tag添加后缀
    public static final String TAG_FOR_NEW_API_SUFFIX = "_newapisuffix";

    /**
     * @param requestBody  如果为null则为get方法（建议使用另一个async方法），否则为post方法（建议仅当作post方法使用）
     * @param tClass
     * @param httpCallback
     * @deprecated 建议使用 {@link Builder}
     */
    public static void async(String url, RequestBody requestBody, final Class<?> tClass, final HttpCallback httpCallback) {
        if (!AppUtils.isNetConnected()) {
            if (httpCallback != null) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        httpCallback.failure(408, neterrorStr);
                    }
                });
                httpCallback.asyncFailure(408, neterrorStr);
            }
            return;
        }
        mOkHttpClient.newCall(getRequest(url, requestBody, TAG)).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                if (call.isCanceled())
                    return;
                if (httpCallback != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            httpCallback.failure(1000, fetchDataErrorStr);
                        }
                    });
                    httpCallback.asyncFailure(1000, fetchDataErrorStr);
                }
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                parseResponse(call, response, httpCallback, tClass);
            }
        });
    }

    /**
     * get方法
     *
     * @param url
     * @param tClass
     * @param httpCallback
     * @deprecated 建议使用 {@link Builder}
     */
    public static void async(String url, final Class<?> tClass, final HttpCallback httpCallback) {
        async(url, null, tClass, httpCallback);
    }

    /**
     * 解析约定
     *
     * @param response
     * @param httpCallback
     * @param tClass
     */
    private static void parseResponse(Call call, Response response, final HttpCallback httpCallback, final Class<?> tClass) {
        try {
            String api = null, str = null;
//            if (call != null && call.isCanceled())
//                return;
            api = response.request().url().toString();
            str = response.body().string();
            if (tClass == null) {//当tClass为null时，data部分的数据完全自己在代码中解析了，就不再使用该HTTP工具类帮忙解析了
                if (httpCallback != null) {
                    final String finalStr = str;
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            httpCallback.success(finalStr);
                        }
                    });
                    httpCallback.asyncSuccess(str);
                }
                return;
            }
            Gson mGson = new Gson();
            JsonParser parser = new JsonParser();
            JsonObject jsonObject = parser.parse(str).getAsJsonObject();
            final JsonElement element = jsonObject.get("data");
            final int status = jsonObject.get("code").getAsInt();//status：0成功 1参数错误 2未登录 3错误
            final String msg = jsonObject.get("message").getAsString();
            if (status == 200) {
                //当设置模型类为HttpStateModel时，我们直接返回状态码跟提示信息
                if (tClass == HttpStateModel.class) {
                    final Object objectResult = mGson.fromJson(str, tClass);
                    if (httpCallback != null) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                httpCallback.success(objectResult);
                            }
                        });
                        httpCallback.asyncSuccess(objectResult);
                    }
                }
                //当设置模型类为EmptyModelClass时，我们直接返回data数据字符串
                if (tClass == EmptyModelClass.class) {
                    if (httpCallback != null) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                httpCallback.success(element == null ? null : element.toString());
                            }
                        });
                        httpCallback.asyncSuccess(element == null ? null : element.toString());
                    }
                    return;
                }
                if (element.isJsonObject()) {
                    final Object objectResult = mGson.fromJson(element, tClass);
                    if (httpCallback != null) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                httpCallback.success(objectResult);
                            }
                        });
                        httpCallback.asyncSuccess(objectResult);
                    }
                } else if (element.isJsonArray()) {
                    JsonArray jsonArray = element.getAsJsonArray();
                    final List<Object> list = new ArrayList<>();
                    int length = jsonArray.size();
                    for (int i = 0; i < length; i++) {
                        list.add(mGson.fromJson(jsonArray.get(i).getAsJsonObject(), tClass));
                    }
                    if (httpCallback != null) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                httpCallback.success(list);
                            }
                        });
                        httpCallback.asyncSuccess(list);
                    }
                }
            } else {
                if (httpCallback != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            httpCallback.failure(status, msg);
                        }
                    });
                    httpCallback.asyncFailure(status, msg);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            if (e instanceof SocketException)
                return;
            if (httpCallback != null) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        httpCallback.failure(1000, fetchDataErrorStr);
                    }
                });
                httpCallback.asyncFailure(1000, fetchDataErrorStr);
            }
        }
    }

    /**
     * @param url
     * @param requestBody 为null是GET方式，否则为POST方式
     * @return
     */

    private static Request getRequest(String url, RequestBody requestBody, String tag) {
        Request.Builder builder = new Request.Builder();
        builder.tag(tag);
        builder.url(url);
//        String cookie = null;
//        try {
//            CookieManager cookieManager = CookieManager.getInstance();
//            cookie = cookieManager.getCookie(Constant.COOKIE_HOST);
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        if (cookie != null) {
//            builder.addHeader("Cookie", cookie);
//        }
        if (!StringUtils.isEmpty(UserUtils.getToken())) {
            builder.addHeader("Authorization", UserUtils.getToken());
        }
        if (requestBody != null)
            builder.post(requestBody);
        return builder.build();
    }

    /**
     * 不管是post还是get都要参与token运算，但是post不将参数拼接在URL后面
     *
     * @param url
     * @param params
     * @param isGetMethod
     * @return
     */
    public static String rebuildUrl(String url, SortedMap params, boolean isGetMethod) {
        if (params == null)
            params = new TreeMap();
        //基本参数集合
        SortedMap customParamsMap = new TreeMap();
        String urlSpliceParams;
        customParamsMap.putAll(params);
        if (!isGetMethod) {
            //post 参数不拼接在URL后面
            for (Object key : params.keySet()) {
                customParamsMap.remove(key);
            }
        }
        urlSpliceParams = buildParams(customParamsMap);
        return url + "?" + urlSpliceParams;
    }

    /**
     * get方法
     *
     * @param url
     * @param params
     * @return
     */
    public static String rebuildUrl(String url, SortedMap params) {
        return rebuildUrl(url, params, true);
    }

    public static String buildParams(SortedMap params) {
        if(params.size()==0){
            return "";
        }
        StringBuilder urlParams = new StringBuilder();
        for (Object key : params.keySet()) {
            if (params.get(key) == null)
                continue;
            urlParams.append(key).append("=");
            try {
                urlParams.append(URLEncoder.encode(params.get(key).toString(), "UTF-8"));
            } catch (UnsupportedEncodingException e) {
            }
            urlParams.append("&");
        }
        return urlParams.toString().substring(0, urlParams.toString().length() - 1);
    }

    /**
     * 下载
     *
     * @param url
     * @param progressListener
     */
    public static void download(String url, final File out, final ProgressResponseBody.ProgressListener progressListener) {
        if (StringUtils.isEmpty(url) || out == null)
            return;
        Request request = new Request.Builder()
                .url(url)
                .build();
        mOkHttpClient.networkInterceptors().add(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Response originalResponse = chain.proceed(chain.request());
                return originalResponse.newBuilder()
                        .body(new ProgressResponseBody(originalResponse.body(), progressListener))
                        .build();
            }
        });
        mOkHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //将返回结果转化为流，并写入文件
                try {
                    int len;
                    byte[] buf = new byte[2048];
                    InputStream inputStream = response.body().byteStream();
                    //可以在这里自定义路径
                    FileOutputStream fileOutputStream = new FileOutputStream(out);
                    while ((len = inputStream.read(buf)) != -1) {
                        fileOutputStream.write(buf, 0, len);
                    }
                    fileOutputStream.flush();
                    fileOutputStream.close();
                    inputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * 取消标记为tag的请求
     *
     * @param tag
     */
    public static void cancel(String tag) {
        try {
//            mOkHttpClient.dispatcher().queuedCalls().stream().filter(call -> tag.equals(call.request().tag())).forEach(Call::cancel);
//            mOkHttpClient.dispatcher().runningCalls().stream().filter(call -> tag.equals(call.request().tag())).forEach(Call::cancel);
            for (int i = 0; i < mOkHttpClient.dispatcher().queuedCallsCount(); i++) {
                Call call = mOkHttpClient.dispatcher().queuedCalls().get(i);
                String callTag = call.request().tag().toString();
                if (tag.equals(callTag) && callTag.contains(TAG_FOR_NEW_API_SUFFIX))
                    call.cancel();
            }
            for (int i = 0; i < mOkHttpClient.dispatcher().runningCallsCount(); i++) {
                Call call = mOkHttpClient.dispatcher().runningCalls().get(i);
                String callTag = call.request().tag().toString();
                if (tag.equals(callTag) && callTag.contains(TAG_FOR_NEW_API_SUFFIX))
                    call.cancel();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void setRequestAndCallback(Request request, Callback callback) {
        mOkHttpClient.newCall(request).enqueue(callback);
    }

    /**
     * 1、不用关心url的内部重构逻辑，只需传普通url即可 <br/>
     * 2、不用关心生成token的参数，只需传所有需要的参数即可,包含数组参数 <br/>
     * 3、默认get方式，调用方法{@link #postMethod()}后变成post方式 <br/>
     * <br/> add version 4.1.3+
     */
    public static class Builder {
        private String url;
        private SortedMap sortedMap;
        private boolean isGetMethod = true;
        private HttpCallback callback;
        private Class<?> modelClass;
        private boolean isJsonContentType = true;
        private String tag;//http tag
        private String json;

        public Builder(Context context) {
            this.tag = context == null ? TAG : context.getClass().getName() + TAG_FOR_NEW_API_SUFFIX;
        }

        /**
         * 普通的url，例如：http://www.shihuo.cn/app_swoole_news/recommend
         *
         * @param url
         * @return
         */
        public Builder url(String url) {
            this.url = url;
            return this;
        }

        /**
         * 传参
         *
         * @param map
         * @return
         */
        public Builder params(SortedMap map) {
            this.sortedMap = map;
            return this;
        }

        /**
         * 设置为post方式，默认get方式
         *
         * @return
         */
        public Builder postMethod() {
            isGetMethod = false;
            return this;
        }

        /**
         * 设置content-type为json格式
         *
         * @return
         */
        public Builder withoutJsonContentType() {
            isJsonContentType = false;
            return this;
        }

        /**
         * add version 5.5.0+<br/>
         * 主要用来兼容传jsonArray格式的参数,如果参数不是jsonArray格式的，可以继续用sortedMap传参（也可以用这个json),当调用postMethod和withJsonContentType后生效(使用gson库)
         *
         * @param jsonStr
         * @return
         */
        public Builder json(String jsonStr) {
            json = jsonStr;
            return this;
        }

        /**
         * 返回的数据模型
         *
         * @param modelClass
         * @return
         */
        public Builder modelClass(Class<?> modelClass) {
            this.modelClass = modelClass;
            return this;
        }

        /**
         * 设置此模型后，会自动判断请求是否成功(避免需要自己解析status)，若成功则会返回data字符串
         *
         * @return
         */
        public Builder emptyModelClass() {
            return modelClass(EmptyModelClass.class);
        }

        public Builder callback(HttpCallback callback) {
            this.callback = callback;
            return this;
        }

        /**
         * 发起网络请求
         */
        public void start() {
            if (!AppUtils.isNetConnected()) {
                if (callback != null) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            callback.failure(408, neterrorStr);
                        }
                    });
                    callback.asyncFailure(408, neterrorStr);
                }
                return;
            }
            if (sortedMap == null)
                sortedMap = new TreeMap();
            url = rebuildUrl(url, sortedMap, isGetMethod);
            FormBody.Builder builder = null;
            RequestBody requestBody = null;
            if (!isGetMethod) {
                if (isJsonContentType) {
                    MediaType JSON = MediaType.parse("application/json; charset=utf-8");
                    if (TextUtils.isEmpty(json)) {
                        Gson gson = new Gson();
                        requestBody = RequestBody.create(JSON, gson.toJson(sortedMap));
                    } else requestBody = RequestBody.create(JSON, json);
                } else {
                    builder = new FormBody.Builder();
                    for (Object key : sortedMap.keySet()) {
                        if (key == null || sortedMap.get(key) == null)
                            continue;
                        builder.add(key.toString(), sortedMap.get(key).toString());
                    }
                }
            }
            if (requestBody == null && builder != null)
                requestBody = builder.build();
            mOkHttpClient.newCall(getRequest(url, requestBody, tag)).enqueue(new Callback() {
                @Override
                public void onFailure(Call call, IOException e) {
                    if (call.isCanceled())
                        return;
                    if (callback != null) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                callback.failure(1000, fetchDataErrorStr);
                            }
                        });
                        callback.asyncFailure(1000, fetchDataErrorStr);
                    }
                    e.printStackTrace();
                }

                @Override
                public void onResponse(Call call, Response response) throws IOException {
                    parseResponse(call, response, callback, modelClass);
                }
            });
        }
    }

    /**
     * 空模型类，标记类，设置该类的目的是，判断status是否为0，为0的话返回data数据
     */
    public static class EmptyModelClass {

    }
}
